package com.cy.sqlSession;

import com.cy.pojo.BoundSql;
import com.cy.pojo.Configuration;
import com.cy.pojo.MappedStatement;
import com.cy.utils.GenericTokenParser;
import com.cy.utils.ParameterMapping;
import com.cy.utils.ParameterMappingTokenHandler;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, NoSuchFieldException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //获取数据库连接
        Connection connection = configuration.getDataSource().getConnection();

        //转换Sql语句中的参数
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);

        //获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());

        //对参数进行绑定
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        Class<?> paramType = mappedStatement.getParamType();

        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            String content = parameterMapping.getContent();

            //使用反射，根据名称获取对象中的属性值
            Field declaredField = paramType.getDeclaredField(content);
            //设置暴力访问，防止字段是private
            declaredField.setAccessible(true);

            Object o = declaredField.get(params[0]);
            //绑定获取的参数到preparedStatement中
            preparedStatement.setObject(i+1, o);
        }

        //执行SQL
        ResultSet resultSet = preparedStatement.executeQuery();

        Class<?> resultType = mappedStatement.getResultType();

        List<Object> resultList = new ArrayList<>();

        //返回结果集的封装
        while (resultSet.next()){
            Object o = resultType.newInstance();

            //获取到元数据，它包含了字段名称
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                //字段名
                String columnName = metaData.getColumnName(i);
                //获取字段值
                Object value = resultSet.getObject(columnName);
                //反射，将值放入类中(此处用的是内省PropertyDescriptor)
                //此处捕获异常，如果某个字段没有对应方法，则跳过
                try {
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultType);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(o, value);
                } catch (IntrospectionException e) {
//                    e.printStackTrace();
                }
            }
            resultList.add(o);
        }

        return (List<E>) resultList;
    }

    /**
     * 更新操作
     * @param configuration
     * @param mappedStatement
     * @param object
     * @param <T>
     * @return
     * @throws SQLException
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    @Override
    public <T> int update(Configuration configuration, MappedStatement mappedStatement, Object... object) throws SQLException, NoSuchFieldException, IllegalAccessException {
        //获取数据库连接
        Connection connection = configuration.getDataSource().getConnection();

        //获取Sql语句中的参数
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);

        //获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());

        //获取到参数对象
        Class<?> paramType = mappedStatement.getParamType();

        //绑定参数
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            String content = parameterMapping.getContent();

            Field declaredField = paramType.getDeclaredField(content);
            //设置强制获取，防止方法是private
            declaredField.setAccessible(true);

            //从参数中获取字段值
            Object o = declaredField.get(object[0]);

            //设置到参数中
            preparedStatement.setObject(i + 1, o);
        }

        //执行SQL
        return preparedStatement.executeUpdate();
    }

    /**
     * 解析SQL并提取参数
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();

        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);

        String parseSql = genericTokenParser.parse(sql);

        return new BoundSql(parseSql, parameterMappingTokenHandler.getParameterMappings());
    }
}
